﻿using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using ePipe;
using eTask.Extensions;
using Letter.IO.Binary;
using Letter.Net.SSL;

namespace Letter.Net.Tcp;

public abstract class ADefaultFixedHeaderBytesConnection : ATcpConnection
{
    delegate int ReadInt32Delegate(ref BytesReader reader);

    private static Dictionary<BinaryOrder, ReadInt32Delegate> readInt32Map = new Dictionary<BinaryOrder, ReadInt32Delegate>()
    {
        {BinaryOrder.BigEndian, (ref BytesReader reader) => { return reader.ReadInt32BE(); }},
        {BinaryOrder.LittleEndian, (ref BytesReader reader) => { return reader.ReadInt32LE(); }},
    };
   
    public ADefaultFixedHeaderBytesConnection(BinaryOrder order) : this(order, null, null)
    {
    }

    public ADefaultFixedHeaderBytesConnection(BinaryOrder order, SSLOptions sslOptions, Func<Stream, SslStream> sslStreamFactory)
        : base(sslOptions, sslStreamFactory)
    {
        if (!readInt32Map.ContainsKey(order))
        {
            throw new Exception("Unsupported order types:"+order.ToString());
        }

        this.readInt32 = readInt32Map[order];
    }

    private const int PACK_HEAD_BYTES_LEN = 4;
    
    private int maxPackLength = int.MaxValue;
    private PackPart currentReadPart = PackPart.HEAD;
    private int currentReadLength = PACK_HEAD_BYTES_LEN;

    private ReadInt32Delegate readInt32;
    
    protected override void OnRcvBuffer(IPipeReader<byte> input, ref ReadOnlySequence<byte> buf)
    {
        var reader = buf.ToBytesReader();
        while (reader.IsLengthEnough(this.currentReadLength))
        {
            if (this.currentReadPart == PackPart.HEAD)
            {
                this.currentReadLength = this.readInt32(ref reader);
                if (this.currentReadLength > this.maxPackLength)
                {
                    throw new Exception("pack length error!" + currentReadLength);
                }

                this.currentReadPart = PackPart.BODY;
            }
            else if (this.currentReadPart == PackPart.BODY)
            {
                this.OnMessage(reader.ReadBuffer(this.currentReadLength));

                this.currentReadLength = PACK_HEAD_BYTES_LEN;
                this.currentReadPart = PackPart.HEAD;
            }
        }

        input.AdvanceTo(reader.End);
        input.Flush();
    }

    protected abstract void OnMessage(ReadOnlySequence<byte> buf);

    public void WriteAndPushAsync(ref ReadOnlySequence<byte> buf)
    {
        if (!this.Active)
        {
            return;
        }
        
        if (buf.Length == 0)
        {
            throw new ArgumentNullException(nameof(buf));
        }

        var output = this.Transport.Writer;
        var writer = output.ToBytesWriter();
        writer.WriteInt32BE((int)buf.Length);
        writer.WriteSequence(ref buf);
        output.FlushAsync().Ignore();
    }
    
    enum PackPart : byte
    {
        HEAD,
        BODY,
    }
}